/*
 * lubbock.c: Intel Lubbock specific stuff
 *
 * Modified from src/blob/assebet.c
 *
 * Copyright (C) 2002  Intel Corporation (yu.tang@intel.com)
 * Copyright (C) 2001  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
# include <blob/config.h>
#endif

#include <blob/arch.h>
#include <blob/flash.h>
#include <blob/init.h>
#include <blob/serial.h>
#include <blob/command.h>
#include <blob/util.h>

#include <strata.h>

#undef SXCNFG_VAL
#define SXCNFG_VAL	0x60F5
#define RCR_VAL		0x28C2

static void k3_sync_mode()
{
	/* Adjust K3.RCR for optimal timing - Lubbock */
	if(BOOT_DEF & 0x1) {
		/* 16 bit, << 1 */
		u16 *rcr = (u16*) (RCR_VAL<<1);
		u16 *base = 0;

		/* read mode */
		*base = 0x00ff;
		barrier();

		/* write RCR value, 1st - 0x0060,  2nd - 0x0003 */
		*rcr = 0x0060;
		barrier();

		*rcr = 0x0003;
		barrier();

		/* read mode */
		*base = 0x00ff;
		barrier();

#if defined(CPU_pxa262)
		base = (u16*) 0x1000000;
		rcr = (u16*) (0x1000000 + (RCR_VAL<<1));

		/* read mode */
		*base = 0x00ff;
		barrier();

		/* write RCR value, 1st - 0x0060,  2nd - 0x0003 */
		*rcr = 0x0060;
		barrier();

		*rcr = 0x0003;
		barrier();

		/* read mode */
		*base = 0x00ff;
		barrier();
#endif
	}
	else {
		/* 32bit, << 2 */
		u32 *rcr = (u32*) (RCR_VAL << 2);
		u32 *base = 0;

		/* read mode */
		*base = 0x00ff00ff;
		barrier();

		/* write RCR value, 1st - 0x0060,  2nd - 0x0003 */
		*rcr = 0x00600060;
		barrier();

		*rcr = 0x00030003;
		barrier();

		/* read mode */
		*base = 0x00ff00ff;
		barrier();
	}

	/* SDCLK0 = memclk/2 */
	MDREFR |= 0x7000;

	/* Set SXCNFG */
	SXCNFG = SXCNFG_VAL;
}

static struct strata_flash_descriptor strata_chips[2];

static void init_lubbock_flash_driver(void)
{
	flash_descriptors = NULL;
	flash_driver = NULL;

#if  ( defined(CPU_pxa250) || defined(CPU_pxa255) )
	/* Bank 0 */
	strata_chips[0].base = 0;
	strata_chips[0].buswidth = (BOOT_DEF & 0x1) ? 2 : 4;


	/* Bank 1 */
	strata_chips[1].base = 0x4000000;
	strata_chips[1].buswidth = (BOOT_DEF & 0x1) ? 2 : 4;

	flash_probe(&strata_chips[0]);
	flash_probe(&strata_chips[1]);

#elif ( defined(CPU_pxa262) )
	strata_chips[0].base = 0;
	strata_chips[0].buswidth = (BOOT_DEF&0x1) ? 2 : 4;;
	strata_chips[1].base = 0x1000000;
	strata_chips[1].buswidth = (BOOT_DEF&0x1) ? 2 : 4;;

	flash_probe(&strata_chips[0]);
	flash_probe(&strata_chips[1]);
#endif

#if defined(CPU_pxa262)
	k3_sync_mode();
#endif

}

__initlist(init_lubbock_flash_driver, INIT_LEVEL_INITIAL_HARDWARE);

struct mem_area
{
	unsigned long start;
	unsigned long size;
	unsigned char attr;
};

static struct mem_area io_map[]={
	{0x00000000, 	0x04000000, 	0x2}, // ROM, 32M
	{0x04000000,	0x04000000, 	0x2}, // Flash
	{0x08000000,    0x00100000, 	0x2}, // CPLD
	{0x0A000000,    0x00100000,     0xE}, // SRAM 
	{0x0C000000,	0x00100000,	0x2}, // SMC I/O
	{0x0E000000,	0x00100000,	0x2}, // SMC Attr
	{0x40000000,	0x04000000,     0x2}, // Memory Map 
	{0x44000000,	0x04000000,     0x2}, // LCD 
	{0x48000000,	0x04000000,     0x2}, // Memory Ctl
	{0xA0000000, 	0x04000000,     0xE} // SDRAM Bank 0 
};

// Large Page, 1M per entry 
#define MAX_PE	4096
static unsigned long mmu_table[MAX_PE] __attribute__ ( (aligned(0x4000)));

static void enable_mmu()
{
	unsigned long i, addr, end;

	/* init MMU table */

	for (i=0; i < MAX_PE; i++) {
		mmu_table[i] = 0;
	}

	for (i=0; i < sizeof(io_map)/sizeof(struct mem_area); i++) {
		addr = io_map[i].start;
		end = addr + io_map[i].size;

		while( addr < end ) {
			mmu_table[addr>>20] = addr | io_map[i].attr;
			addr += 0x100000;
		}
	}

	/* enable MMU */
	__asm(
	"
	MRC p15, 0, R0, c1, c0
	ORR R0, R0, #0x1000 
	ORR R0, R0, #0x800 
	MCR p15, 0, R0, c1, c0, 0 

	mrc p15, 0, R0, c2, c0, 0
	mov R0, R0
	sub PC, PC, #4

    	nop
    	nop
    	nop
    	nop	

	ldr	r0, =mmu_table
	mcr p15, 0, r0, c2, c0 
	mrc p15, 0, R0, c2, c0, 0
	mov R0, R0
	sub PC, PC, #4

    	nop
    	nop
    	nop
    	nop	
	
	mvn r0, #0 
	mcr p15, 0, r0, c3, c0, 0  
	mrc p15, 0, R0, c2, c0, 0
	mov R0, R0
	sub PC, PC, #4

    	nop
    	nop
    	nop
    	nop	
	
	MCR p15, 0, r0, c7, c10, 4
	MRC p15, 0, r0, c1, c0, 0
	ORR r0, r0, #4 		
	MCR p15, 0, r0, c1, c0, 0	

	mrc p15, 0, R0, c2, c0, 0
	mov R0, R0
	sub PC, PC, #4

    	nop
    	nop
    	nop
    	nop	

	MRC p15,0,R0,c1,c0,0
	ORR R0, R0, #0x1
	MCR p15,0,R0,c1,c0,0		

	mrc p15, 0, R0, c2, c0, 0
	mov R0, R0
	sub PC, PC, #4

    	nop
    	nop
    	nop
    	nop	
	"
	);

}

static void lubbock_init_hardware(void)
{

	/* select serial driver */
	serial_driver = &pxa_serial_driver;

	/* turn on MMU, to better perforamce */
	enable_mmu();
}


__initlist(lubbock_init_hardware, INIT_LEVEL_DRIVER_SELECTION);

/*********************************************************************
 * cmd_flash_write
 *
 * AUTOR:	SELETZ
 * REVISED:
 *
 * Command wrapper for low-level flash write access
 *
 */
static int cmd_flash_write( int argc, char *argv[] )
{
	int ret = 0;
	u32	src	= 0L;
	u32	dest	= 0L;
	u32	len	= 0L;
	struct strata_flash_descriptor *chip=NULL;

	if ( argc < 4 ) {
		ret = -1;
		goto DONE;
	}

	ret = strtou32( argv[1], &src );
	if ( ret < 0 ) {
		ret = -1;
		goto DONE;
	}

	ret = strtou32( argv[2], &dest );
	if ( ret < 0 )	{
		ret = -1;
		goto DONE;
	}

	ret = strtou32( argv[3], &len );
	if ( ret < 0 ) {
		ret = -1;
		goto DONE;
	}

#if  ( defined(CPU_pxa250) || defined(CPU_pxa255) )
	if (dest < 0x4000000) 
		chip = &strata_chips[0];
	else
		chip = &strata_chips[1];

#elif defined(CPU_pxa262)
	chip = &strata_chips[0];

	if(dest >= 0x4000000) {
		ret = -1;
		goto DONE;
	}
	else if (dest >= 0x1000000) {
		chip = &strata_chips[1];
	}
	else if ( (dest+len) > 0x1000000) {
		int datalen = 0x1000000 - dest;

		chip = &strata_chips[0];
		ret = flash_write(chip, dest, (u8*)src, datalen);
		if (ret) return ret;

		chip = &strata_chips[1];
		dest = 0x1000000;
		src += datalen;
		len  -= datalen;
	}
#endif

	ret = flash_write(chip, dest, (u8*) src, len); 

DONE:
	return ret;
}


static char flashwritehelp[] = "fwrite srcadr destadr size(bytes)\n"
"flash a memory region\n";
__commandlist( cmd_flash_write, "fwrite", flashwritehelp );

/*********************************************************************
 * cmd_flash_erase
 *
 * AUTOR:	SELETZ
 * REVISED:
 *
 * Command wrapper for low-level flash erasing
 *
 */
static int cmd_flash_erase( int argc, char *argv[] )
{
	int ret = 0;
	u32	dest	= 0L;
	u32	len	= 0L;
	struct strata_flash_descriptor *chip=NULL;

	if ( argc < 3 ) {
		ret = -1;
		goto DONE;
	}

	ret = strtou32( argv[1], &dest );
	if ( ret < 0 ) {
		ret = -1;
		goto DONE;
	}	

	ret = strtou32( argv[2], &len );
	if ( ret < 0 ){
		ret = -1;
		goto DONE;
	}

#if  ( defined(CPU_pxa250) || defined(CPU_pxa255) )
	if (dest < 0x4000000) 
		chip = &strata_chips[0];
	else
		chip = &strata_chips[1];
#elif defined(CPU_pxa262)
	chip = &strata_chips[0];

	if(dest >= 0x4000000) {
		ret = -1;
		goto DONE;
	}
	else if (dest >= 0x1000000) {
		chip = &strata_chips[1];
	}
	else if ( (dest+len) > 0x1000000) {
		int len0 = 0x1000000 - dest;

		chip = &strata_chips[0];
		ret = flash_erase(chip, dest, len0);
		if (ret) return ret;

		chip = &strata_chips[1];
		dest = 0x1000000;
		len  -= len0;
	}
#endif

	ret = flash_erase(chip, dest, len);

DONE:
	return ret;
}

static char flasherasehelp[] = "ferase adr size(bytes)\n"
"erase a flash region\n";
__commandlist( cmd_flash_erase, "ferase", flasherasehelp );



